module uart_tx
#(
    parameter CLK_FREQ = 50000000,
    parameter UART_BPS = 115200
)
(
    input               clk         , //系统时钟
    input               rst_n       , //系统复位，低有效
    input               en  ,      //发送使能,单次发送:__↑￣|_20ms_,持续发送:_↑￣￣^^￣￣￣|__
    input     [7:0]     din_data, //要发送的数据
    output  reg         txd    ,    //发送端口
    output  reg         dout_busy  //发送忙状态信号
    );

localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率，对系统时钟计数BPS_CNT次


reg  [7:0]  tx_data_temp;  //发送数据寄存器


reg  [15:0] cnt_baud ;  //波特率计数器
wire cnt_baud_add= cnt_baud < BAUD_CNT_MAX - 1'b1;
wire cnt_baud_end=dout_busy==0;


reg  [3:0]  cnt_tx   ;  //发送数据计数器
wire cnt_tx_add=cnt_baud == BAUD_CNT_MAX - 1'b1; //en持续为1,则cnt_tx_add会溢出到0,重复发
wire cnt_tx_end=dout_busy==0;


//当uart_tx_en为高时，寄存输入的并行数据，并拉高BUSY信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        tx_data_temp <= 8'b0;
        dout_busy <= 1'b0;
    end
    //发送使能时，寄存要发送的数据，并拉高BUSY信号
    else if(en) begin
        tx_data_temp <= din_data;
        dout_busy <= 1'b1;
    end
    //当计数到停止位结束时，停止发送过程
    else if(cnt_tx == 4'd9 && cnt_baud == BAUD_CNT_MAX - 1) begin
        tx_data_temp <= 8'b0;     //清空发送数据寄存器
        dout_busy <= 1'b0;  //并拉低BUSY信号
    end
    else begin
        tx_data_temp <= tx_data_temp;
        dout_busy <= dout_busy;
    end
end

//波特率的计数器赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
         cnt_baud <= 16'd0;
    else if(cnt_baud_end) begin
         cnt_baud <= 16'd0;
    end
    else if(cnt_baud_add)
         cnt_baud <= cnt_baud + 16'b1;
    else
         cnt_baud <= 16'd0;
end

//cnt_tx进行赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_tx <= 4'd0;
    else if(cnt_tx_end) begin             //处于发送过程时cnt_tx才进行计数
        cnt_tx <= 4'd0;                     //发送过程结束时计数器清零
    end else
        if(cnt_tx_add)
            cnt_tx <= cnt_tx + 1'b1;
        else
            cnt_tx <= cnt_tx;
    end

//根据cnt_tx来给uart发送端口赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        txd <= 1'b1;
    else if(dout_busy) begin
        case(cnt_tx)
            4'd0 : txd <= 1'b0        ; //起始位
            4'd1 : txd <= tx_data_temp[0]; //数据位最低位
            4'd2 : txd <= tx_data_temp[1];
            4'd3 : txd <= tx_data_temp[2];
            4'd4 : txd <= tx_data_temp[3];
            4'd5 : txd <= tx_data_temp[4];
            4'd6 : txd <= tx_data_temp[5];
            4'd7 : txd <= tx_data_temp[6];
            4'd8 : txd <= tx_data_temp[7]; //最高位
            4'd9 : txd <= 1'b1        ; //停止位
            default : txd <= 1'b1;
        endcase
    end
    else
        txd <= 1'b1;                   
end

endmodule